home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / misc / btngo.zip / BTNGOC.ZIP / LONCH.CPP < prev    next >
C/C++ Source or Header  |  1993-09-28  |  32KB  |  750 lines

  1. // DEBUG_SCREENS for message-box progress indicators
  2. // FAKE_LAUNCHES for message boxes instead of actual launches
  3. // SHOW_LAUNCHDATA for quick display just before prog launch -- works only if FAKE_LAUNCHES *not* specified
  4. // CHANGE_DIR to change directory to entry's default before launch attempted
  5. #include<windows.h>
  6. #include<commdlg.h>
  7. #include"wstring.hpp"
  8. #include"itemdata.hpp"
  9. #include"resource.h"
  10. #include"ini.hpp"
  11. #include<string.h>
  12. #include<ctype.h>
  13. #ifndef FAKE_LAUNCHES
  14. #include<direct.h>
  15. #endif
  16.  
  17. #define IDM_GETSTARTED 1
  18.  
  19. #define IDM_ABOUT 1001
  20. #define IDM_HELP 1002
  21. #define IDM_ONTOP 1003
  22. #define IDM_MYSYSCOMMANDS 1010
  23.  
  24. #define BUTTON_WIDTH (icon_width+5)
  25.  
  26. IniFile ini;
  27. GroupFile gf;
  28. #ifdef DEBUG_SCREENS
  29. WinMessageString screen;
  30. #endif
  31. WinMessageString errscreen;
  32. const int SCRATCH=256;
  33. unsigned int winver; // windows version
  34. char scratch[SCRATCH+1];
  35. static char WindowTitle[256];
  36. static char szFile[256]; // filename for .GRP file
  37. static HWND main_hwnd=NULL;
  38. static HINSTANCE main_hinst=NULL;
  39. static char AppTitle[]="ButtonGo";
  40. static char AppClassName[]="ButtonGoDeurbrouckClass";
  41. static char copyright[]=
  42.     "BTNGO.EXE 1.0\n"
  43.     "Program Launching Utility\n"
  44.     "Copyright ⌐ 1993 John Deurbrouck\n\n"
  45.     "First published in PC Magazine September 28, 1993\n";
  46. static char helpstring[]=
  47.     "BTNGO conveniently displays the icons from any Program Manager group "
  48.     "and allows you to launch them with a single mouse click.\n\n"
  49.     "If started with a .GRP file specified on the command line, BTNGO can operate under "
  50.     "Windows 3.0 and later. Otherwise, BTNGO requires COMMDLG.DLL to run under 3.0.\n\n"
  51.     "See PC Magazine, September 28, 1993 (Vol. 12 No. 15) for more details. Support is also available "
  52.     "on ZiffNet. From Compuserve, GO ZNT:UTILFORUM.";
  53. static char too_many_icons[]=
  54.     "The group you have loaded has too many icons to display, "
  55.     "so some of your icons may not be visible. "
  56.     "No damage has occurred, and you may use the icons you can see.\n\n"
  57.     "You can fix this by removing less-important items from the group, or by "
  58.     "creating another group file and moving some icons from this group "
  59.     "to the new one.";
  60. static LPSTR command_line_pointer;
  61. static HMENU gm=NULL; // system menu for main window
  62. static HBITMAP hbmUp=NULL,hbmDown=NULL,hbmNow=NULL; // for painting the main window
  63. static unsigned int max_index_added=0;
  64. static int show_parameter; // for call to ShowWindow, parm passed into WinMain
  65. static int actual_buttons=0; // number buttons on button bar
  66. static int icon_height,icon_width,bitmap_width,bitmap_height;
  67. static int mouse_down=0,mouse_button_number=-1,ok_to_launch; // used to drive mouse handling
  68. static int should_save_position_data=0,got_position_data,best_ontop=1,best_x,best_y; // used for .INI file handling
  69.  
  70. void GetFilenameFromCmdLine(LPSTR cmdline,char *winpath,char *filename);
  71. long FAR PASCAL __export MainWndProc(HWND,UINT,UINT,LONG);
  72. int GetFileDataLoaded(LPSTR cmdline);
  73. int CreateBitmaps(void);
  74. int button_mouse_is_in(LONG lParam);
  75. void restore_mouse_normalcy(void);
  76. void get_right_bitmap(HDC hdc);
  77. #ifdef FAKE_LAUNCHES
  78. char *show_exe_data(int offset);
  79. #else
  80. void start_program(int offset);
  81. #endif
  82.  
  83. int PASCAL WinMain(HINSTANCE hinstCurrent,HINSTANCE hinstPrevious,LPSTR lpszCmdLine,int nCmdShow){
  84.     if(!hinstPrevious){
  85.         WNDCLASS wc;
  86.         wc.style=CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
  87.         wc.lpfnWndProc=MainWndProc;
  88.         wc.cbClsExtra=0;
  89.         wc.cbWndExtra=0;
  90.         wc.hInstance=hinstCurrent;
  91.         wc.hIcon=LoadIcon(hinstCurrent,MAKEINTRESOURCE(IDI_APPICON));
  92.         wc.hCursor=LoadCursor(NULL,IDC_ARROW);
  93.         wc.hbrBackground=NULL;
  94.         wc.lpszMenuName=NULL;
  95.         wc.lpszClassName=AppClassName;
  96.         RegisterClass(&wc);
  97.     }
  98.     {
  99.         DWORD ver=GetVersion();
  100.         winver=LOBYTE(LOWORD(ver));
  101.         winver<<=8;
  102.         winver|=HIBYTE(LOWORD(ver));
  103.     }
  104.     command_line_pointer=lpszCmdLine;
  105.     main_hinst=hinstCurrent;
  106.     show_parameter=nCmdShow;
  107.     ini.set_instance(hinstCurrent);
  108.     main_hwnd=CreateWindow(AppClassName,AppTitle,
  109.                 WS_OVERLAPPED|WS_SYSMENU|WS_MINIMIZEBOX,
  110.                 CW_USEDEFAULT,CW_USEDEFAULT, // X,Y coords
  111.                 CW_USEDEFAULT,CW_USEDEFAULT, // x,y extents
  112.                 HWND_DESKTOP,NULL,hinstCurrent,NULL);
  113.     #ifdef DEBUG_SCREENS
  114.     screen.SetHwndParent(main_hwnd);
  115.     #endif
  116.     errscreen.SetHwndParent(main_hwnd);
  117.     MSG msg;
  118.     while(GetMessage(&msg,NULL,0,0)){
  119.         TranslateMessage(&msg);
  120.         DispatchMessage(&msg);
  121.     }
  122.     return msg.wParam;
  123. }
  124.  
  125. static void GetFilenameFromCmdLine(LPSTR cmdline,char *winpath,char *filename){
  126.     LPSTR string_start;
  127.     int got_pathchars=0,got_goodchars=0,got_whitespace=0;
  128.     if(cmdline==NULL)return;
  129.     while(*cmdline){  // scan, find filename start, look for '\\' and ':'
  130.         if(isgraph(*cmdline)){
  131.             if(got_whitespace){
  132.                 errscreen<<"Too many command line arguments";
  133.                 errscreen.show();
  134.                 return;
  135.             }
  136.             if(!got_goodchars){
  137.                 got_goodchars=1;
  138.                 string_start=cmdline;
  139.             }
  140.             if(*cmdline=='\\'||*cmdline==':')got_pathchars=1;
  141.             if(*cmdline=='*'||*cmdline=='?'){
  142.                 errscreen<<"Can't use wildcards in command line arguments";
  143.                 errscreen.show();
  144.                 return;
  145.             }
  146.         }
  147.         else{
  148.             if(got_goodchars)got_whitespace=1;
  149.         }
  150.         cmdline++;
  151.     }
  152.     if(!got_goodchars)return; // no filename
  153.     if(!got_pathchars){  // insert Windows dir, normal place for .GRP
  154.         lstrcpy(filename,winpath);
  155.         cmdline=filename;
  156.         while(*cmdline)cmdline++;
  157.         if(cmdline[-1]!='\\'){*cmdline++='\\';*cmdline=0;}
  158.         lstrcat(filename,string_start);
  159.     }
  160.     else lstrcpy(filename,string_start);
  161.     // now scan off any whitespace at end of filename
  162.     cmdline=filename;
  163.     while(*cmdline)cmdline++;
  164.     cmdline--;
  165.     while(!isgraph(*cmdline))*cmdline--=0;
  166. }
  167. long FAR PASCAL __export MainWndProc(HWND hwnd,UINT message,UINT wParam,LONG lParam){
  168.     switch(message){
  169.     case WM_CREATE:
  170.         PostMessage(hwnd,WM_COMMAND,IDM_GETSTARTED,0L);
  171.         return 0;
  172.     case WM_COMMAND:
  173.         switch(wParam){
  174.         case IDM_GETSTARTED:
  175.             gm=GetSystemMenu(main_hwnd,FALSE);
  176.             DeleteMenu(gm,SC_SIZE,MF_BYCOMMAND);
  177.             DeleteMenu(gm,SC_MAXIMIZE,MF_BYCOMMAND);
  178.             AppendMenu(gm,MF_SEPARATOR,0,NULL);
  179.             if(winver>=0x30A)AppendMenu(gm,MF_STRING,IDM_ONTOP,"Always on Top");
  180.             AppendMenu(gm,MF_STRING,IDM_ABOUT,"About...");
  181.             AppendMenu(gm,MF_STRING,IDM_HELP,"Help...");
  182.             if(!GetFileDataLoaded(command_line_pointer)||
  183.             !gf.get_icon_dimensions(icon_height,icon_width)||
  184.             !CreateBitmaps()){
  185.                 PostMessage(hwnd,WM_DESTROY,0,0L);
  186.                 return 0;
  187.             }
  188.             if(gf.get_group_name()!=NULL){
  189.                 wsprintf(WindowTitle,"%s - %s",(LPSTR)AppTitle,gf.get_group_name());
  190.             }
  191.             else lstrcpy(WindowTitle,AppTitle);
  192.             gf.done_with_bitmaps();
  193.             SetWindowText(hwnd,WindowTitle);
  194.             should_save_position_data=1;
  195.             got_position_data=ini.get_screen_numbers(szFile,best_x,best_y,best_ontop);
  196.             if(winver>=0x30A&&best_ontop)PostMessage(hwnd,WM_SYSCOMMAND,IDM_ONTOP,0L);
  197.             int window_width=GetSystemMetrics(SM_CXBORDER)*2+bitmap_width;
  198.             int window_height=GetSystemMetrics(SM_CYCAPTION)+bitmap_height+GetSystemMetrics(SM_CYBORDER);
  199.             /*
  200.                 ok, someone changing from 1024x768 to 800x600 drivers might 'lose' the
  201.                 window so let's be sure the window's at least partially onscreen.
  202.                 if at least one pixel of the caption area is onscreen, the window
  203.                 can be grabbed and moved, so let's check for that
  204.             */
  205.             if(got_position_data)for(;;){
  206.                 got_position_data=0; // so can break on errors
  207.                 int capt_left=best_x+GetSystemMetrics(SM_CXBORDER)+GetSystemMetrics(SM_CXSIZE)+2;
  208.                 int capt_right=best_x+window_width-GetSystemMetrics(SM_CXSIZE)-2;
  209.                 int x_size=GetSystemMetrics(SM_CXSCREEN);
  210.                 if(!(capt_left<0&&capt_right>=x_size)){ // ok, straddles screen
  211.                     if(capt_right<0)break;
  212.                     if(capt_left>=x_size)break;
  213.                 }
  214.                 int capt_top=best_y+GetSystemMetrics(SM_CYBORDER)+2;
  215.                 int capt_bottom=best_y+GetSystemMetrics(SM_CYCAPTION)-2;
  216.                 int y_size=GetSystemMetrics(SM_CYSCREEN);
  217.                 if(capt_top>=y_size)break;
  218.                 if(capt_bottom<0)break;
  219.                 got_position_data=1;
  220.                 break;
  221.             }
  222.             if(got_position_data){
  223.                 SetWindowPos(hwnd,0,best_x,best_y,
  224.                     window_width,window_height,
  225.                     SWP_NOACTIVATE|SWP_NOZORDER);
  226.             }
  227.             else{
  228.                 SetWindowPos(hwnd,0,0,0,
  229.                     window_width,window_height,
  230.                     SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
  231.             }
  232.             int window_is_truncated=0;
  233.             {
  234.                 RECT temprect;
  235.                 GetWindowRect(hwnd,&temprect);
  236.                 if((temprect.right-temprect.left)!=window_width){
  237.                     window_is_truncated=1;
  238.                 }
  239.             }
  240.             ShowWindow(hwnd,show_parameter);
  241.             UpdateWindow(hwnd);
  242.             if(window_is_truncated){
  243.                 MessageBox(hwnd,too_many_icons,"Window Creation Error",MB_OK);
  244.             }
  245.             return 0;
  246.         }
  247.         break;
  248.     case WM_SYSCOMMAND:
  249.         if(wParam>=IDM_MYSYSCOMMANDS&&wParam<=(IDM_MYSYSCOMMANDS+max_index_added)){
  250.             #ifdef FAKE_LAUNCHES
  251.             MessageBox(hwnd,show_exe_data(wParam-IDM_MYSYSCOMMANDS),"Doing a command",MB_OK|MB_ICONINFORMATION);
  252.             #else
  253.             start_program(wParam-IDM_MYSYSCOMMANDS);
  254.             #endif
  255.             return 0;
  256.         }
  257.         else switch(wParam){
  258.         case IDM_ABOUT:
  259.             MessageBox(hwnd,copyright,"About ButtonGo",MB_OK|MB_ICONINFORMATION);
  260.             return 0;
  261.         case IDM_HELP:
  262.             MessageBox(hwnd,helpstring,"ButtonGo Help",MB_OK|MB_ICONINFORMATION);
  263.             return 0;
  264.         case IDM_ONTOP:
  265.             if(MF_CHECKED&GetMenuState(gm,IDM_ONTOP,MF_BYCOMMAND)){
  266.                 CheckMenuItem(gm,IDM_ONTOP,MF_BYCOMMAND|MF_UNCHECKED);
  267.                 SetWindowPos(hwnd,HWND_NOTOPMOST,0,0,0,0,SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE);
  268.             }
  269.             else{
  270.                 CheckMenuItem(gm,IDM_ONTOP,MF_BYCOMMAND|MF_CHECKED);
  271.                 SetWindowPos(hwnd,HWND_TOPMOST,0,0,0,0,SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE);
  272.             }
  273.             return 0;
  274.         }
  275.         break;
  276.     case WM_LBUTTONDOWN:
  277.         {
  278.             int b=button_mouse_is_in(lParam);
  279.             if(b!=-1){
  280.                 mouse_button_number=b;
  281.                 wsprintf(scratch,"%s (%s)",(LPSTR)gf.get_item_name(b),(LPSTR)gf.get_item_command_nopath(b));
  282.                 SetWindowText(hwnd,scratch);
  283.                 mouse_down=1;
  284.                 ok_to_launch=1;
  285.                 InvalidateRect(hwnd,NULL,FALSE);
  286.                 UpdateWindow(hwnd);
  287.                 SetCapture(hwnd);
  288.                 return 0;
  289.             }
  290.         }
  291.         break;
  292.     case WM_CANCELMODE:
  293.         if(mouse_down){
  294.             restore_mouse_normalcy();
  295.             return 0;
  296.         }
  297.         break;
  298.     case WM_MOUSEMOVE:
  299.         if(mouse_down){
  300.             int b=button_mouse_is_in(lParam);
  301.             if(b!=mouse_button_number){
  302.                 ok_to_launch=0;
  303.                 mouse_button_number=b;
  304.                 if(b!=-1){
  305.                     wsprintf(scratch,"%s (%s)",(LPSTR)gf.get_item_name(b),(LPSTR)gf.get_item_command_nopath(b));
  306.                     SetWindowText(hwnd,scratch);
  307.                 }
  308.                 else{
  309.                     SetWindowText(hwnd,WindowTitle);
  310.                 }
  311.                 InvalidateRect(hwnd,NULL,FALSE);
  312.                 UpdateWindow(hwnd);
  313.             }
  314.             return 0;
  315.         }
  316.         break;
  317.     case WM_LBUTTONUP:
  318.         if(mouse_down){
  319.             int b=button_mouse_is_in(lParam),its_a_go=0;
  320.             if(ok_to_launch&&b==mouse_button_number){
  321.                 its_a_go=1;
  322.             }
  323.             restore_mouse_normalcy();
  324.             if(its_a_go){
  325.                 #ifdef FAKE_LAUNCHES
  326.                 MessageBox(hwnd,show_exe_data(b),"Mousing a command",MB_OK|MB_ICONINFORMATION);
  327.                 #else
  328.                 start_program(b);
  329.                 #endif
  330.             }
  331.             return 0;
  332.         }
  333.         break;
  334.     case WM_PAINT:
  335.         {
  336.             PAINTSTRUCT ps;
  337.             HDC hdc=BeginPaint(hwnd,&ps);
  338.             if(hdc!=NULL){
  339.                 HDC hdcmem=CreateCompatibleDC(hdc);
  340.                 if(hdcmem!=NULL){
  341.                     HBITMAP hbmOld=SelectObject(hdcmem,hbmNow);
  342.                     get_right_bitmap(hdcmem);
  343.                     BitBlt(hdc,0,0,bitmap_width,bitmap_height,hdcmem,0,0,SRCCOPY);
  344.                     SelectObject(hdcmem,hbmOld);
  345.                     DeleteDC(hdcmem);
  346.                 }
  347.             }
  348.             EndPaint(hwnd,&ps);
  349.         }
  350.         return 0;
  351.     case WM_DESTROY:
  352.         if(hbmUp  !=NULL){DeleteObject(hbmUp  );hbmUp  =NULL;}
  353.         if(hbmDown!=NULL){DeleteObject(hbmDown);hbmDown=NULL;}
  354.         if(hbmNow !=NULL){DeleteObject(hbmNow );hbmNow =NULL;}
  355.         if(should_save_position_data){
  356.             ini.write_screen_numbers(szFile,hwnd,MF_CHECKED&GetMenuState(gm,IDM_ONTOP,MF_BYCOMMAND)?1:0);
  357.         }
  358.         PostQuitMessage(0);
  359.         return 0;
  360.     case WM_ENDSESSION:
  361.         if(wParam&&should_save_position_data){
  362.             ini.write_screen_numbers(szFile,hwnd,MF_CHECKED&GetMenuState(gm,IDM_ONTOP,MF_BYCOMMAND)?1:0);
  363.         }
  364.         else break;
  365.         return 0;
  366.     }
  367.     return DefWindowProc(hwnd,message,wParam,lParam);
  368. }
  369. int GetFileDataLoaded(LPSTR lpszCmdLine){
  370.     OPENFILENAME ofn;
  371.     char szDirName[256];
  372.     GetWindowsDirectory(szDirName,sizeof(szDirName));
  373.     szFile[0]=0;
  374.     GetFilenameFromCmdLine(lpszCmdLine,szDirName,szFile);
  375.     if(szFile[0]==0){
  376.         memset(&ofn,0,sizeof(ofn));
  377.         ofn.lStructSize=sizeof(ofn);
  378.         ofn.hwndOwner=main_hwnd;
  379.         ofn.lpstrFilter="Group Files\0*.GRP\0";
  380.         ofn.nFilterIndex=1;
  381.         ofn.lpstrFile=szFile;
  382.         ofn.nMaxFile=sizeof(szFile);
  383.         ofn.lpstrInitialDir=szDirName;
  384.         ofn.lpstrTitle="Choose .GRP file for ButtonGo";
  385.         ofn.Flags=OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
  386.         ofn.lpstrDefExt="GRP";
  387.         // let's make sure COMMDLG.DLL is here so unfriendly system message doesn't show up
  388.         // on GetOpenFileName() call in Win 3.0
  389.         // doing LoadLibrary()/GetProcAddress() shuffle to avoid needing COMMDLG.DLL just to load
  390.         int got_commdlg=0;
  391.         UINT old_sem=SetErrorMode(SEM_NOOPENFILEERRORBOX);
  392.         HINSTANCE h=LoadLibrary("COMMDLG.DLL");
  393.         if(h>=HINSTANCE_ERROR){ // ok the DLL is there
  394.             BOOL(WINAPI*lpGetOpenFileName)(OPENFILENAME FAR*)=(BOOL(WINAPI*)(OPENFILENAME FAR*))GetProcAddress(h,"GetOpenFileName");
  395.             if(lpGetOpenFileName!=NULL){
  396.                 got_commdlg=1;
  397.                 if(!(*lpGetOpenFileName)(&ofn)){
  398.                     FreeLibrary(h);
  399.                     SetErrorMode(old_sem);
  400.                     return NULL; // user canceled, so bail out...
  401.                 }
  402.             }
  403.             FreeLibrary(h);
  404.         }
  405.         SetErrorMode(old_sem);
  406.         if(!got_commdlg){
  407.             errscreen<<"COMMDLG.DLL should be in your WINDOWS\\SYSTEM directory\n\n";
  408.             errscreen<<"Until you get that file installed, you'll have to specify a .GRP ";
  409.             errscreen<<"filename on BtnGo's command line";
  410.             errscreen.show("Quitting");
  411.             return NULL;
  412.         }
  413.     }
  414.     #ifdef DEBUG_SCREENS
  415.     screen<<"File is \""<<szFile<<"\"\n";
  416.     #endif
  417.     { // get to all caps so .INI file works right (same every time even if typed)
  418.         char *cp=szFile;
  419.         while(*cp){
  420.             *cp=toupper((int)*cp);
  421.             cp++;
  422.         }
  423.     }
  424.     gf.set_filename(szFile);
  425.     if(!gf.openfile()){
  426.         errscreen<<"File is "<<szFile<<"\n"<<"Could not open file\n";
  427.         errscreen.show();
  428.         return NULL;
  429.     }
  430.     gf.readheader();
  431.     #ifdef DEBUG_SCREENS
  432.     screen<<"header read was "<<(gf.error()?"not ":"")<<"successful\n";
  433.     screen<<"there are "<<gf.itemsleft()<<" items to process\n";
  434.     #endif
  435.     int added_some=0;
  436.     for(int x=0;x<gf.itemsleft();x++){
  437.         if(gf.error()){
  438.             errscreen<<"aborting due to error\n";
  439.             errscreen.show();
  440.             break;
  441.         }
  442.         if(gf.showitem(x)){
  443.             actual_buttons++;
  444.             char __far *ptr=gf.get_item_name(x);
  445.             while(ptr!=NULL&&isspace(*ptr))ptr++;
  446.             if(ptr!=NULL&&*ptr){ // don't add to system menu if name was all spaces
  447.                 if(!(added_some%9))AppendMenu(gm,MF_MENUBARBREAK,0,NULL);
  448.                 added_some++;
  449.                 AppendMenu(gm,MF_STRING,IDM_MYSYSCOMMANDS+x,ptr);
  450.                 max_index_added=max_index_added>(unsigned int)x?max_index_added:(unsigned int)x;
  451.             }
  452.         }
  453.     }
  454.     #ifdef DEBUG_SCREENS
  455.     screen.show("All Done");
  456.     #endif
  457.     return 1;
  458. }
  459. int CreateBitmaps(void){
  460.     /*
  461.         This function creates a set of three bitmaps. The first bitmap, the handle to which is
  462.         called hbmUp, is the bitmap that show all the buttons in their up (normal) state.
  463.         The second, hbmDown, shows all the buttons in their down state. The last, hbmNow, is
  464.         the *current* picture, which is normally all up (a copy of hbmUp) but may include
  465.         exactly one button from hbmDown.
  466.  
  467.         The bitmaps are created to fit exactly into the client area of the main window.
  468.         So though the main window has NULL for hbrBackground, the area should look fine due
  469.         to WM_PAINT always repainting the entire thing. The lack of erase should reduce flicker.
  470.     */
  471.     int width,height=icon_height+5,retval=0;
  472.     HDC hdc=NULL,memhdc=NULL,icondc=NULL,bwdc=NULL;
  473.     HBRUSH hbrFace=NULL,hbrOld=NULL;
  474.     HPEN hpFace=NULL,hpHighlight=NULL,hpShadow=NULL,hpBlack=NULL,hpOld=NULL;
  475.     HBITMAP hbmOld=NULL;
  476.     {
  477.         int temp=GetSystemMetrics(SM_CXMIN);   // window can't be arbitrarily small
  478.         width=actual_buttons*BUTTON_WIDTH;
  479.         width=width>temp?width:temp;
  480.     }
  481.     bitmap_width=width;
  482.     bitmap_height=height;
  483.     // create bitmaps
  484.     hbmUp=CreateBitmap(width,height,gf.get_icon_planes(),gf.get_icon_pixelbits(),NULL);
  485.     hbmDown=CreateBitmap(width,height,gf.get_icon_planes(),gf.get_icon_pixelbits(),NULL);
  486.     hbmNow=CreateBitmap(width,height,gf.get_icon_planes(),gf.get_icon_pixelbits(),NULL);
  487.     if(hbmUp==NULL||hbmDown==NULL||hbmNow==NULL)return 0;
  488.     // create display context
  489.     if((hdc=CreateDC("DISPLAY",NULL,NULL,NULL))==NULL)goto bailout;
  490.     // create compatible DC and select 'up' bitmap
  491.     if(NULL==(memhdc=CreateCompatibleDC(hdc)))goto bailout;
  492.     hbmOld=SelectObject(memhdc,hbmUp);
  493.     // create DC for icon
  494.     icondc=CreateCompatibleDC(hdc);
  495.     if(icondc==NULL)goto bailout;
  496.     // create brush for button faces and select it
  497.     if(NULL==(hbrFace     =CreateSolidBrush(GetSysColor(COLOR_BTNFACE     ))))goto bailout;
  498.     hbrOld=SelectObject(memhdc,hbrFace);
  499.     // create pens for black, button faces, edges, select face color
  500.     if(NULL==(hpBlack=GetStockObject(BLACK_PEN)))goto bailout;
  501.     if(NULL==(hpFace     =CreatePen(PS_SOLID,1,GetSysColor(COLOR_BTNFACE     ))))goto bailout;
  502.     if(winver>=0x30A){
  503.         if(NULL==(hpHighlight=CreatePen(PS_SOLID,1,GetSysColor(COLOR_BTNHIGHLIGHT))))goto bailout;
  504.     }
  505.     else{
  506.         if(NULL==(hpHighlight=CreatePen(PS_SOLID,1,RGB(255,255,255))))goto bailout;
  507.     }
  508.     if(NULL==(hpShadow   =CreatePen(PS_SOLID,1,GetSysColor(COLOR_BTNSHADOW   ))))goto bailout;
  509.     hpOld=SelectObject(memhdc,hpFace);
  510.     Rectangle(memhdc,0,0,width,height); // faces, up map
  511.     {                                // button edges, up map
  512.         int f_ofset,x_offset,x_size,y_size;
  513.         for(f_ofset=0,x_offset=0;f_ofset<actual_buttons;f_ofset++,x_offset+=(5+icon_width)){
  514.             int oldstretchmode=SetStretchBltMode(memhdc,STRETCH_DELETESCANS);
  515.             { // get AND data
  516.                 HBITMAP x=gf.get_fill_item_and_bitmap(f_ofset,x_size,y_size);
  517.                 if(x!=NULL){
  518.                     x=SelectObject(icondc,x); // save old one...
  519.                     StretchBlt(memhdc,x_offset+2,2,icon_width,icon_height,icondc,0,0,x_size,y_size,SRCAND);
  520.                     x=SelectObject(icondc,x); // get new one back
  521.                     DeleteObject(x);        // and delete it
  522.                 }
  523.             }
  524.             { // get XOR data
  525.                 HBITMAP x=gf.get_fill_item_xor_bitmap(f_ofset,x_size,y_size);
  526.                 if(x!=NULL){
  527.                     x=SelectObject(icondc,x); // save old one...
  528.                     StretchBlt(memhdc,x_offset+2,2,icon_width,icon_height,icondc,0,0,x_size,y_size,SRCINVERT);
  529.                     x=SelectObject(icondc,x); // get new one back
  530.                     DeleteObject(x);        // and delete it
  531.                 }
  532.             }
  533.             SetStretchBltMode(memhdc,oldstretchmode);
  534.             SelectObject(memhdc,hpBlack); // draw black lines
  535.             MoveTo(memhdc,x_offset              ,1);
  536.             LineTo(memhdc,x_offset              ,icon_height+4);
  537.             MoveTo(memhdc,x_offset+icon_width+4 ,1);
  538.             LineTo(memhdc,x_offset+icon_width+4 ,icon_height+4);
  539.             MoveTo(memhdc,x_offset+1            ,0);
  540.             LineTo(memhdc,x_offset+icon_width+4 ,0);
  541.             MoveTo(memhdc,x_offset+1            ,icon_height+4);
  542.             LineTo(memhdc,x_offset+icon_width+4 ,icon_height+4);
  543.             SelectObject(memhdc,hpHighlight); // draw highlights
  544.             MoveTo(memhdc,x_offset+1            ,1);
  545.             LineTo(memhdc,x_offset+icon_width+3 ,1);
  546.             MoveTo(memhdc,x_offset+1            ,2);
  547.             LineTo(memhdc,x_offset+1            ,icon_height+3);
  548.             SelectObject(memhdc,hpShadow); // draw shadows
  549.             MoveTo(memhdc,x_offset+icon_width+2 ,2);
  550.             LineTo(memhdc,x_offset+icon_width+2 ,icon_height+4);
  551.             MoveTo(memhdc,x_offset+icon_width+3 ,1);
  552.             LineTo(memhdc,x_offset+icon_width+3 ,icon_height+4);
  553.             MoveTo(memhdc,x_offset+2            ,icon_height+2);
  554.             LineTo(memhdc,x_offset+icon_width+2 ,icon_height+2);
  555.             MoveTo(memhdc,x_offset+1            ,icon_height+3);
  556.             LineTo(memhdc,x_offset+icon_width+2 ,icon_height+3);
  557.         }
  558.     }
  559.     // now do 'down' bitmap
  560.     SelectObject(memhdc,hbmDown);
  561.     SelectObject(memhdc,hpFace);
  562.     Rectangle(memhdc,0,0,width,height); // faces, down map
  563.     {                                // button edges, down map
  564.         int f_ofset,x_offset,x_size,y_size;
  565.         for(f_ofset=0,x_offset=0;f_ofset<actual_buttons;f_ofset++,x_offset+=(5+icon_width)){
  566.             int shrinkage=(icon_width*2)/10,oldstretchmode=SetStretchBltMode(memhdc,STRETCH_DELETESCANS);
  567.             { // get AND data
  568.                 HBITMAP x=gf.get_fill_item_and_bitmap(f_ofset,x_size,y_size);
  569.                 if(x!=NULL){
  570.                     x=SelectObject(icondc,x); // save old one...
  571.                     StretchBlt(memhdc,x_offset+2+shrinkage,2+shrinkage,icon_width-shrinkage,icon_height-shrinkage,icondc,0,0,x_size,y_size,SRCAND);
  572.                     x=SelectObject(icondc,x); // get new one back
  573.                     DeleteObject(x);        // and delete it
  574.                 }
  575.             }
  576.             { // get XOR data
  577.                 HBITMAP x=gf.get_fill_item_xor_bitmap(f_ofset,x_size,y_size);
  578.                 if(x!=NULL){
  579.                     x=SelectObject(icondc,x); // save old one...
  580.                     StretchBlt(memhdc,x_offset+2+shrinkage,2+shrinkage,icon_width-shrinkage,icon_height-shrinkage,icondc,0,0,x_size,y_size,SRCINVERT);
  581.                     x=SelectObject(icondc,x); // get new one back
  582.                     DeleteObject(x);        // and delete it
  583.                 }
  584.             }
  585.             SetStretchBltMode(memhdc,oldstretchmode);
  586.             SelectObject(memhdc,hpBlack); // draw black lines
  587.             MoveTo(memhdc,x_offset              ,1);
  588.             LineTo(memhdc,x_offset              ,icon_height+4);
  589.             MoveTo(memhdc,x_offset+icon_width+4 ,1);
  590.             LineTo(memhdc,x_offset+icon_width+4 ,icon_height+4);
  591.             MoveTo(memhdc,x_offset+1            ,0);
  592.             LineTo(memhdc,x_offset+icon_width+4 ,0);
  593.             MoveTo(memhdc,x_offset+1            ,icon_height+4);
  594.             LineTo(memhdc,x_offset+icon_width+4 ,icon_height+4);
  595.             SelectObject(memhdc,hpShadow); // draw shadows
  596.             MoveTo(memhdc,x_offset+1            ,1);
  597.             LineTo(memhdc,x_offset+icon_width+4 ,1);
  598.             MoveTo(memhdc,x_offset+1            ,2);
  599.             LineTo(memhdc,x_offset+1            ,icon_height+4);
  600.             MoveTo(memhdc,x_offset+2            ,2);
  601.             LineTo(memhdc,x_offset+icon_width+4 ,2);
  602.             MoveTo(memhdc,x_offset+2            ,3);
  603.             LineTo(memhdc,x_offset+2            ,icon_height+4);
  604.         }
  605.     }
  606.     retval=1;
  607. bailout:
  608.     if(icondc!=NULL)DeleteDC(icondc);
  609.     if(hpShadow!=NULL)SelectObject(memhdc,hpOld);
  610.     if(hpShadow   !=NULL)DeleteObject(hpShadow   );
  611.     if(hpHighlight!=NULL)DeleteObject(hpHighlight);
  612.     if(hpFace     !=NULL)DeleteObject(hpFace     );
  613.     if(hbrFace!=NULL){
  614.         SelectObject(memhdc,hbrOld);
  615.         DeleteObject(hbrFace);
  616.     }
  617.     if(memhdc!=NULL){
  618.         SelectObject(memhdc,hbmOld);
  619.         DeleteDC(memhdc);
  620.     }
  621.     if(hdc!=NULL)DeleteDC(hdc);
  622.     return retval;
  623. }
  624. int button_mouse_is_in(LONG lParam){
  625.     if(actual_buttons==0||bitmap_width==0)return -1;
  626.     int x=(int)LOWORD(lParam);
  627.     int y=(int)HIWORD(lParam);
  628.     if(y<0||y>=bitmap_height||x<0||x>=bitmap_width)return -1;
  629.     x/=BUTTON_WIDTH;
  630.     if(x>=actual_buttons)return -1;
  631.     return x;
  632. }
  633. void restore_mouse_normalcy(void){
  634.     if(mouse_down){
  635.         ReleaseCapture();
  636.         mouse_down=0;
  637.         SetWindowText(main_hwnd,WindowTitle);
  638.         InvalidateRect(main_hwnd,NULL,FALSE);
  639.         UpdateWindow(main_hwnd);
  640.     }
  641. }
  642. void get_right_bitmap(HDC hdc){
  643.     /*
  644.         draws appropriate bitmap into hdc if necessary, a memory DC
  645.         the right-sized bitmap is already there
  646.     */
  647.     HDC hdc_source;
  648.     HBITMAP hbmOld;
  649.     if(!mouse_down||mouse_button_number==-1||
  650.     !bitmap_width||!actual_buttons||
  651.     (0==(hdc_source=CreateCompatibleDC(hdc)))){
  652.         // just poke in the 'Up' bitmap
  653.         SelectObject(hdc,hbmUp);
  654.     }
  655.     else{
  656.         int left_edge=mouse_button_number*BUTTON_WIDTH;
  657.         // first draw all 'up' buttons
  658.         hbmOld=SelectObject(hdc_source,hbmUp);
  659.         BitBlt(hdc,0,0,bitmap_width,bitmap_height,hdc_source,0,0,SRCCOPY);
  660.         // now poke in the single 'down' button
  661.         SelectObject(hdc_source,hbmDown);
  662.         BitBlt(hdc,left_edge,0,BUTTON_WIDTH,bitmap_height,hdc_source,left_edge,0,SRCCOPY);
  663.         SelectObject(hdc_source,hbmOld);
  664.         DeleteDC(hdc_source);
  665.     }
  666. }
  667. #ifdef FAKE_LAUNCHES
  668. char *show_exe_data(int offset){
  669.     static char buf[300];
  670.     wsprintf(buf,"Name \"%s\"\nExecutable \"%s\"\nDefault Directory \"%s\"\n%s minimized",
  671.     gf.get_item_name(offset),
  672.     gf.get_command(offset),
  673.     gf.get_item_path(offset),
  674.     (LPSTR)(gf.is_item_iconized(offset)?"Runs":"Does not run"));
  675.     return buf;
  676. }
  677. #else
  678. void start_program(int offset){
  679.     // launch the program, minimized if appropriate
  680.     // report failures to the user
  681.     UINT retval=0;
  682.     if(winver>=0x30A){
  683. #ifdef SHOW_LAUNCHDATA
  684.         errscreen<<"Ready to start\nget_item_command()=\""<<gf.get_item_command(offset);
  685.         errscreen<<"\"\nget_item_parameters()=\""<<gf.get_item_parameters(offset);
  686.         errscreen<<"\"\nget_item_default_dir()=\""<<gf.get_item_default_dir(offset);
  687.         errscreen<<"\"\nitem is"<<(gf.is_item_iconized(offset)?"":" not")<<" iconized";
  688.         errscreen.show();
  689. #endif
  690.         UINT old_error_mode=SetErrorMode(SEM_NOOPENFILEERRORBOX);
  691.         HINSTANCE s=LoadLibrary("SHELL.DLL");
  692.         HINSTANCE (WINAPI*lpShellExecute)(HWND,LPCSTR,LPCSTR,LPCSTR,LPCSTR,int);
  693.         if(s>=HINSTANCE_ERROR)lpShellExecute=(HINSTANCE(WINAPI*)(HWND,LPCSTR,LPCSTR,LPCSTR,LPCSTR,int))GetProcAddress(s,"ShellExecute");
  694.         if(s>=HINSTANCE_ERROR&&lpShellExecute!=NULL){
  695.             retval=(*lpShellExecute)(main_hwnd,NULL,
  696.                 gf.get_item_command(offset),gf.get_item_parameters(offset),
  697.                 gf.get_item_default_dir(offset),
  698.                 gf.is_item_iconized(offset)?SW_SHOWMINIMIZED:SW_SHOWNORMAL);
  699.         }
  700.         if(s>=HINSTANCE_ERROR)FreeLibrary(s);
  701.         SetErrorMode(old_error_mode);
  702.     }
  703.     else{
  704.         char buffer[250],*t;
  705.         retval=2;
  706.         LPSTR from=gf.get_item_default_dir(offset);
  707.         if(from!=NULL){ // use path we stripped off originally
  708.             lstrcpy(buffer,from);
  709.             t=buffer;
  710.             while(*t)t++;
  711.             if(t[-1]!=':'&&t[-1]!='\\'){
  712.                 *t++='\\';
  713.                 *t=0;
  714.             }
  715.             lstrcpy(t,gf.get_item_command(offset));
  716.             retval=WinExec(buffer,gf.is_item_iconized(offset)?SW_SHOWMINNOACTIVE:SW_SHOW);
  717.         }
  718.         if(retval==2){ // use 3.1-style command if present
  719.             retval=WinExec(gf.get_item_command(offset),gf.is_item_iconized(offset)?SW_SHOWMINNOACTIVE:SW_SHOW);
  720.         }
  721.         if((retval==2)&&(gf.get_item_command(offset)!=gf.get_item_command_nopath(offset))){ // .EXE name only
  722.             retval=WinExec(gf.get_item_command_nopath(offset),gf.is_item_iconized(offset)?SW_SHOWMINNOACTIVE:SW_SHOW);
  723.         }
  724.     }
  725.     if(retval>=32)return; //success!
  726.     char *p;
  727.     switch(retval){
  728.     case  0: p="System memory shortage or corrupt executable file"; break;
  729.     case  2: p="File was not found"; break;
  730.     case  3: p="Path was not found"; break;
  731.     case  5: p="Attempt to dynamically link to a task, or sharing or network-protection error"; break;
  732.     case  6: p="Library required separate data segments for each task"; break;
  733.     case  8: p="Insufficient memory to start application"; break;
  734.     case 10: p="Application can't run on this version of Windows"; break;
  735.     case 11: p="Invalid executable file. Not a Windows application, or .EXE image error"; break;
  736.     case 12: p="Application designed for a different operating system"; break;
  737.     case 13: p="Application was designed for MS-DOS 4.0"; break;
  738.     case 14: p="Unknown executable type"; break;
  739.     case 15: p="Cannot run real-mode Windows applications"; break;
  740.     case 16: p="Can't run multiple copies of this program (non-read-only multiple data segments)"; break;
  741.     case 19: p="File must be decompressed before it is loaded"; break;
  742.     case 20: p="One of the DLLs required to run this application was corrupt or otherwise invalid"; break;
  743.     case 21: p="Application requires 32-bit extensions to Windows"; break;
  744.     default: p="Unknown error"; break;
  745.     }
  746.     errscreen<<"Could not start "<<gf.get_item_command(offset)<<"\n\n"<<p;
  747.     errscreen.show();
  748. }
  749. #endif
  750.